Positives
-
Many! This has been my favorite language so far.
-
It's the most fun I had with a language!
-
Solves many problems I had with Zig, Rust, C/C++.
-
(2025-04-20) I really like the big focus on NAMES in the syntax :
-
I found the syntax very weird initially, but the reality is it is ultra intuitive and I have liked it a lot.
my_var := 123 my_proc :: proc() {}
-
-
(2025-04-20) No need for
;and don't miss it :-
I never missed
;, after all what made the experience positive are the{ }, not the;. -
You can use
;if you want, though.
-
-
(2025-04-20) No need for
( )in expressions :-
Much better than in Zig and C, nice.
-
-
(2025-04-20) Enum access is simple, similar to Swift :
-
Can be used as
.Ainstead ofMyEnum.A, in the correct context.
-
-
(2025-04-20) No need to specify return type for
voidwhen there is no return :-
Nice.
-
-
(2025-04-20) No methods :
-
Great.
-
-
(2025-04-20) Excellent built system :
-
Anything compared to C/C++ is excellent, to be fair.
-
Either way, it's by fair the easy language to compile I've seen.
-
-
(2025-04-20) The package system really seems very good, with folders :
-
Inspired by Go.
-
After seeing Ginger Bill's explanation in this video {26:50 -> 34:30} , I found it very nice.
-
Really seems to be a very good solution for managing exports/imports.
-
Negatives
-
I explored "fixing" a lot of these negatives in my Odin fork ; check the README.md for an overview of the changes.
-
"fixing" is quoted as some people may argue that some changes were bad, but of course, that depends on how you enjoy programming and how much you care about safety and explicitness.
-
-
(2025-12-12) I don't like the
contextsystem at all .-
I have lots of critiques around it.
-
See Context for a in-depth discussion about this.
-
-
(2025-12-13) I don't like
@(init)and@(fini).-
Quoting a snippet from the discord thread about that I found interesting and agree with:
-
Barinzaya:
-
I think
@(init)procs are kind of an anti-pattern. I dislike the "this proc is now always going to be called whether you want it or not" nature of it.
-
-
Caio:
-
I completely agree. The only reason I used
@(init)in this situation, was because other libraries do. I had to place the profiler earlier than all of them, so the only way to do it is by also being@(init)or going before than_startup_runtime.
-
-
-
(2025-11-13) I don't like how
@(require_results)is NOT the default way of handling results; I would prefer the opposite .-
By default, errors can be ignored. Not good. Things like
#optional_okand#optional_allocator_errorexists, but the main problem is actually how@(require_results)is optional. By the default a procedure will not require the results to be handled. I wish the opposite was true: you have to opt out of required results and use something like@(optional_results); the priorities should have been inverted. -
There's also the annoyance of having to add
@(require_results)for every math function and similar, etc. -
I made a suggestion for something like
#+vet explicit-returns, as way to have every unhandled return be treated as an error, even for#optional_okor#optional_allocator_error, as well as a compiler flag. This would just be an optional flag, per file (even tho I prefer per library), but it was denied :/
-
-
(2025-11-13) I don't like the implicit usage of
context.allocatoraround A LOT of libraries, basically being the standard in Odin .-
This has led me to more bugs that it has helped anything.
-
Also, this leads to code that focuses heavily on "constructors" and "destructors", as by default the
context.allocatoris aruntime.heap_allocator(), which is just a wrap aroundmalloc.-
Some libraries are ok with you using an arena in its place, but other libraries use
defer delete()implicitly and that makes it incompatible with a more straight forward and optimized design of managing memory, focused on lifetimes with arenas.
-
-
Currently, to improve this:
-
I use
panic_allocatoras the default forcontext.allocator, by using the-default-to-panic-allocatorflag. I don't ever reassign thecontext.allocator. I use it as thepanic_allocatorduring all the application, so if I forget to be explicit about an allocation, the app crashes. This is far from perfect, as this is a runtime check, but it's better than losing track of your memory. -
I use
#+vet explicit-allocatorson top of every file. This make it soallocator := context.allocatorgives an error. Sonew(int)will give an error, butnew(int, allocator)will not. Also not perfect, as I'd prefer this to be a compilation flag, etc.
-
-
Both improvements above just hides the problem a bit. I don't like how I had to go around one of the main language design just so I have safer and sane code.
-
Even if
contextwas removed, code could go fromallocator := context.allocatortoallocator := runtime.allocator(thread local global variable, as I suggested). So it's not much of acontextthing, but more about how the language heavily favors design of default allocators, and implicitness. -
I can think of this either being solved by removing default parameters in procedures, or having a code style that enforces explicit allocators instead of the opposite.
-
-
(2025-11-13) I would prefer if there was no default parameters in procedures .
-
This sounds a bit wild, but I came to realize how little I actually need default parameters.
-
They result in implicit behavior, which I believe leads to worse code.
-
Meanwhile, working without default parameters is actually an interesting challenge to solve that I think results in much better APIs.
-
-
(2025-04-20) I don't like
usingoutside of structs :-
Ginger Bill also considers this a mistake.
-
Read the
usingsection for more information.
-
-
(2025-04-20)
Lack of keywords for concurrency is somewhat annoying (async/await) .-
Maybe this is ok, but I do have to investigate a bit more about this.
-
(2025-11-13)
-
Well, I made a library for that, so problem solved. I much rather having my library then using something built in the language now, I think.
-
-
-
(2025-04-20)
Down-casting can be complex :-
I cannot compare subtypes, like in GDScript, with
is:
func _detect_hitbox(area: Area2D) -> void: if not (area is Hitbox): Debug.red('(%s | Hurtbox) The area is not a Hitbox.' % _name) return-
It is necessary to use advanced idioms, with Unions / Enums, etc., to get the desired information.
-
See Odin#Advanced Idioms, Down-Cast and Up-Cast for more information.
-
(2025-11-13)
-
I think I'm ok with this. It's actually really rare I have to use something like the code shown in GDScript, and avoiding these situations led the code to be more understandable.
-
It's a lower level thing, but once you get used to it, I think it's ok.
-
-
-
(2025-04-20)
Having to use:->for function return-
Minor, but I feel it could be hidden.
-
(2025-07-03)
-
Genuinely, I don't care at all.
-
-
-
(2025-04-20)
Having to use:casekeyword for switches-
Minor, but I think the keyword shouldn't exist.
-
(2025-07-03)
-
Genuinely, I don't care at all.
-
I actually kind of like it.
-
-
-
(2025-04-20)
Having to use:prockeyword for procedures-
Ultra minor, I got used to the keyword and it's convenient when considering how similar the syntax is to:
my_proc :: proc() {} my_struct :: struct {} -
(2025-07-03)
-
JAI opts not to use the keyword, but I have come to appreciate its use.
-
-